﻿////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  © 2012 Stanislav S. Yarmonov.
//
//  TODO:LICENSE
//     
////////////////////////////////////////////////////////////////////////////////////////////////////
using System;
using System.Data;
using System.Collections.Generic;

namespace Pandora.Data
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    /// <summary>
    /// Extensions for IDataReader interface.
    /// </summary>
    ////////////////////////////////////////////////////////////////////////////////////////////////
    public static class IDataReaderExtensions
    {
        #region -- Data retrieving routines --------------------------------------------------------
        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Get single record from database reader into instance of selected type.
        /// </summary>
        /// <typeparam name="T">
        /// Type of the instance to be returned.
        /// </typeparam>
        /// <param name="reader">
        /// Database reader to get the data from.
        /// </param>
        /// <param name="mapping">
        /// Routine to map from database reader values into instance of the selected type.
        /// </param>
        /// <returns>
        /// Returns instance of the type taken from database reader.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        public static T Get<T>(this IDataReader reader, DbReaderMapping<T> mapping)
        {
            return reader.Read() ? mapping(reader) : default(T);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Get records from database reader into list of selected type's instances.
        /// </summary>
        /// <typeparam name="T">
        /// Type of the instance to be returned.
        /// </typeparam>
        /// <param name="reader">
        /// Database reader to get the data from.
        /// </param>
        /// <param name="mapping">
        /// Routine to map from database reader values into instance of the selected type.
        /// </param>
        /// <returns>
        /// Returns objects list.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        public static List<T> Select<T>(this IDataReader reader, DbReaderMapping<T> mapping)
        {
            List<T> result = new List<T>();

            while (reader.Read())
            {
                T item = mapping(reader);
                result.Add(item);
            }

            return result;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Apply action for each row in the reader.
        /// </summary>
        /// <param name="reader">
        /// Database reader to get the data from.
        /// </param>
        /// <param name="action">
        /// Action to be applied.
        /// </param>
        /// <returns>
        /// Returns objects list.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        public static void Each(this IDataReader reader, Action<IDataReader> action)
        {
            while (reader.Read())
            {
                action(reader);
            }
        }
        #endregion ---------------------------------------------------------------------------------

        #region -- Data values routines ------------------------------------------------------------
        /// <summary>
        /// TODO:
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        internal static int FindField(this IDataReader reader, string fieldName)
        {
            for (int index = 0; index < reader.FieldCount; index++)
            {
                if (string.Equals(fieldName, reader.GetName(index), StringComparison.InvariantCultureIgnoreCase))
                {
                    return index;
                }
            }

            return -1;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Getting value-type from database reader.
        /// </summary>
        /// <typeparam name="T">
        /// Type to get from database reader.
        /// </typeparam>
        /// <param name="reader">
        /// Database reader to get the value from.
        /// </param>
        /// <param name="fieldName">
        /// Field name to get the value from.
        /// </param>
        /// <returns>
        /// Returns the value, taken from database reader.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        internal static T? GetValue<T>(this IDataReader reader, string fieldName)
            where T : struct
        {
            object value = reader[fieldName];
            return value == null || value == DBNull.Value ? null : (T?)value;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Getting value-type from database reader.
        /// </summary>
        /// <typeparam name="T">
        /// Type to get from database reader.
        /// </typeparam>
        /// <param name="reader">
        /// Database reader to get the value from.
        /// </param>
        /// <param name="fieldName">
        /// Field name to get the value from.
        /// </param>
        /// <param name="default">
        /// Default value if database value is null.
        /// </param>
        /// <returns>
        /// Returns the value, taken from database reader.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        internal static T GetValue<T>(this IDataReader reader, string fieldName, T @default)
            where T : struct
        {
            object value = reader[fieldName];
            return value == null || value == DBNull.Value ? @default : (T)value;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Getting object-type from database reader.
        /// </summary>
        /// <typeparam name="T">
        /// Type to get from database reader.
        /// </typeparam>
        /// <param name="reader">
        /// Database reader to get the value from.
        /// </param>
        /// <param name="fieldName">
        /// Field name to get the value from.
        /// </param>
        /// <returns>
        /// Returns the value, taken from database reader.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        internal static T GetObject<T>(this IDataReader reader, string fieldName)
            where T : class
        {
            int index = reader.FindField(fieldName);
            if (index >= 0)
            {
                object value = reader[index];
                return value == null || value == DBNull.Value ? null : (T)value;
            }
            else
            {
                return null;
            }
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Getting object-type from database reader.
        /// </summary>
        /// <typeparam name="T">
        /// Type to get from database reader.
        /// </typeparam>
        /// <param name="reader">
        /// Database reader to get the value from.
        /// </param>
        /// <param name="fieldName">
        /// Field name to get the value from.
        /// </param>
        /// <param name="default">
        /// Default value if database value is null.
        /// </param>
        /// <returns>
        /// Returns the value, taken from database reader.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        internal static T GetObject<T>(this IDataReader reader, string fieldName, T @default)
            where T : class
        {
            object value = reader[fieldName];
            return value == null || value == DBNull.Value ? @default : (T)value;
        }
        #endregion ---------------------------------------------------------------------------------

        #region -- Int32 ---------------------------------------------------------------------------
        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Get the Int32 value from database reader.
        /// </summary>
        /// <param name="reader">
        /// Database reader to get the value from.
        /// </param>
        /// <param name="fieldName">
        /// Field name to get the value from.
        /// </param>
        /// <returns>
        /// Returns the value, taken from database reader.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        public static Int32? GetInt32(this IDataReader reader, string fieldName)
        {
            return reader.GetValue<Int32>(fieldName);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Get the Int32 value from database reader.
        /// </summary>
        /// <param name="reader">
        /// Database reader to get the value from.
        /// </param>
        /// <param name="fieldName">
        /// Field name to get the value from.
        /// </param>
        /// <param name="default">
        /// Default value if database value is null.
        /// </param>
        /// <returns>
        /// Returns the value, taken from database reader.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        public static Int32 GetInt32(this IDataReader reader, string fieldName, Int32 @default)
        {
            return reader.GetValue<Int32>(fieldName, @default);
        }
        #endregion ---------------------------------------------------------------------------------

        #region -- UInt32 --------------------------------------------------------------------------
        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Get the UInt32 value from database reader.
        /// </summary>
        /// <param name="reader">
        /// Database reader to get the value from.
        /// </param>
        /// <param name="fieldName">
        /// Field name to get the value from.
        /// </param>
        /// <returns>
        /// Returns the value, taken from database reader.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        public static UInt32? GetUInt32(this IDataReader reader, string fieldName)
        {
            return reader.GetValue<UInt32>(fieldName);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Get the Int32 value from database reader.
        /// </summary>
        /// <param name="reader">
        /// Database reader to get the value from.
        /// </param>
        /// <param name="fieldName">
        /// Field name to get the value from.
        /// </param>
        /// <param name="default">
        /// Default value if database value is null.
        /// </param>
        /// <returns>
        /// Returns the value, taken from database reader.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        public static UInt32 GetUInt32(this IDataReader reader, string fieldName, UInt32 @default)
        {
            return reader.GetValue<UInt32>(fieldName, @default);
        }
        #endregion ---------------------------------------------------------------------------------

        #region -- Int64 ---------------------------------------------------------------------------
        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Get the Int64 value from database reader.
        /// </summary>
        /// <param name="reader">
        /// Database reader to get the value from.
        /// </param>
        /// <param name="fieldName">
        /// Field name to get the value from.
        /// </param>
        /// <returns>
        /// Returns the value, taken from database reader.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        public static Int64? GetInt64(this IDataReader reader, string fieldName)
        {
            return reader.GetValue<Int64>(fieldName);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Get the Int64 value from database reader.
        /// </summary>
        /// <param name="reader">
        /// Database reader to get the value from.
        /// </param>
        /// <param name="fieldName">
        /// Field name to get the value from.
        /// </param>
        /// <param name="default">
        /// Default value if database value is null.
        /// </param>
        /// <returns>
        /// Returns the value, taken from database reader.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        public static Int64 GetInt64(this IDataReader reader, string fieldName, Int64 @default)
        {
            return reader.GetValue<Int64>(fieldName, @default);
        }
        #endregion ---------------------------------------------------------------------------------

        #region -- String --------------------------------------------------------------------------
        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Get the string value from database reader.
        /// </summary>
        /// <param name="reader">
        /// Database reader to get the value from.
        /// </param>
        /// <param name="fieldName">
        /// Field name to get the value from.
        /// </param>
        /// <returns>
        /// Returns the value, taken from database reader.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        public static string GetString(this IDataReader reader, string fieldName)
        {
            return reader.GetObject<string>(fieldName);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// TODO:
        /// </summary>
        /// <param name="reader">
        /// Database reader to get the value from.
        /// </param>
        /// <param name="fieldName">
        /// Field name to get the value from.
        /// </param>
        /// <param name="default">
        /// Default value if database value is null.
        /// </param>
        /// <returns>
        /// Returns the value, taken from database reader.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        public static string GetString(this IDataReader reader, string fieldName, string @default)
        {
            return reader.GetObject<string>(fieldName, @default);
        }
        #endregion ---------------------------------------------------------------------------------

        #region -- Binary --------------------------------------------------------------------------
        ////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Get the binary value from database reader.
        /// </summary>
        /// <param name="reader">
        /// Database reader to get the value from.
        /// </param>
        /// <param name="fieldName">
        /// Field name to get the value from.
        /// </param>
        /// <returns>
        /// Returns the value, taken from database reader.
        /// </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////
        public static byte[] GetBytes(this IDataReader reader, string fieldName)
        {
            return reader.GetObject<byte[]>(fieldName);
        }
        #endregion ---------------------------------------------------------------------------------
    }
}
